<template>
  <div class="custom-upload">
    <template v-if="type === 'dropdown'">
      <el-dropdown :size="size" split-button type="default" trigger="click"
        :class="{ 'hide-list': fileList.length <= 0 }">
        <el-upload :name="name" style="width: 100%" :headers="headers" :action="action" :data="data" :limit="limit"
          :disabled="disabled || isLoading" :multiple="multiple" :accept="accept" :show-file-list="false"
          :file-list="fileList" :before-upload="beforeUpload" :before-remove="beforeRemove" :on-exceed="onUploadLimit"
          :on-success="onUploadSuccess" :on-remove="onRemoveFile" :on-error="onUploadError">
          <el-row type="flex" align="middle">
            <span class="upload-btn">
              <el-icon v-if="isLoading" class="is-loading">
                <Loading />
              </el-icon>
              {{ isLoading ? '上传中...' : '上传文件' }}
              <div class="file-count" v-if="uploadWidget.fileList.length > 0">
                {{ fileList.length }}
              </div>
            </span>
          </el-row>
        </el-upload>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item v-for="(item, index) in fileList" :key="item.url">
              <el-row class="dropdown-file-item" type="flex" justify="space-between" align="middle"
                style="height: 36px">
                <span @click="onPreviewClick(item, fileList)" style="flex-grow: 1">
                  {{ item.name }}
                </span>
                <el-icon v-if="!disabled" style="margin-left: 8px"
                  @click.stop="onRemoveFile(item, uploadWidget.fileList.value)">
                  <CircleCloseFilled />
                </el-icon>
              </el-row>
              <div v-if="index !== fileList.length - 1" style="height: 1px; background: #eeeff1" />
            </el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </template>
    <el-upload v-else-if="type === 'expand'" class="expand-upload" :class="itemType" :name="name" style="width: 100%"
      :headers="headers" :action="action" :data="data" :limit="limit"
      :disabled="disabled || isLoading || (limit != null && fileList.length >= limit)" :multiple="multiple"
      :accept="accept" :show-file-list="false" :file-list="fileList" :before-upload="beforeUpload"
      :before-remove="beforeRemove" :on-exceed="onUploadLimit" :on-success="onUploadSuccess" :on-remove="onRemoveFile"
      :on-error="onUploadError">
      <el-row v-if="itemType === 'card'" type="flex" style="flex-wrap: wrap">
        <div ref="uploadBtn" style="width: 0px; height: 0px; border: none" />
      </el-row>
      <div v-else class="upload-file-list">
        <!-- 文本样式 -->
        <el-button :size="size" :disabled="disabled || isLoading || (limit != null && fileList.length >= limit)"
          type="primary" :loading="isLoading">
          {{ isLoading ? '上传中...' : '点击上传' }}
        </el-button>
      </div>
    </el-upload>
    <el-row v-if="type === 'expand' && itemType === 'card'" type="flex" style="flex-wrap: wrap">
      <!-- 卡片样式 -->
      <UploadFileList :supportPreview="supportPreview" :supportDownload="supportDownload" :file-list="fileList"
        :type="itemType" :direction="direction" :supportAdd="showCardAddBtn" @remove="onDeleteFile" @add="onAddFile">
        <div v-if="showCardAddBtn">
          <el-icon style="font-size: 18px">
            <Plus />
          </el-icon>
        </div>
      </UploadFileList>
    </el-row>
    <UploadFileList v-if="type === 'expand' && itemType === 'text'" :supportPreview="supportPreview"
      :supportDownload="supportDownload" :file-list="fileList" :type="itemType" :direction="direction"
      style="margin-top: 8px" @remove="onDeleteFile" />
    <PreviewDialog v-if="showPreview" :visible="showPreview" :file-list="fileList" :defaultIndex="fileIndex"
      :readonly="!supportDownload" @close="showPreview = false" />
  </div>
</template>

<script setup lang="ts">
import { ElMessage } from 'element-plus';
import { defineProps, withDefaults, defineEmits, ref, computed, watch, nextTick } from 'vue';
import { Plus, CircleCloseFilled, Loading } from '@element-plus/icons-vue';
import { ANY_OBJECT } from '@/types/generic';
import { useDownload } from '@/common/hooks/useDownload';
import { useUpload } from '@/common/hooks/useUpload';
import { useUploadWidget } from '@/common/hooks/useUploadWidget';
import UploadFileList from '@/components/UploadFileList/index.vue';
import PreviewDialog from '@/components/Preview/Dialog.vue';
import { getFileType } from '@/common/utils';

type UploadFiles = UploadFile[];
type UploadUserFile = Omit<UploadFile, 'status' | 'uid'> &
  Partial<Pick<UploadFile, 'status' | 'uid'>>;
type UploadStatus = 'ready' | 'uploading' | 'success' | 'fail';
type Awaitable<T> = Promise<T> | T;
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
interface UploadRawFile extends File {
  uid: number;
}

interface UploadFile {
  name: string;
  percentage?: number;
  status: UploadStatus;
  size?: number;
  response?: unknown;
  uid: number;
  url?: string;
  raw?: UploadRawFile;
}

const { downloadFile } = useDownload();
const { getUploadHeaders, getUploadActionUrl, parseUploadData, fileListToJson } = useUpload();

const props = withDefaults(
  defineProps<{
    modelValue?: Array<{ url: string; name: string; type: string }>;
    size: string;
    type: string;
    itemType: string;
    direction: string;
    supportPreview: boolean;
    supportDownload: boolean;
    limit: number;
    disabled: boolean;
    action: string;
    headers: ANY_OBJECT;
    multiple: boolean;
    data: ANY_OBJECT;
    name: string;
    accept?: string;
    beforeUpload?: (
      rawFile: UploadRawFile,
    ) => Awaitable<void | undefined | null | boolean | File | Blob>;
    beforeRemove?: (uploadFile: UploadFile, uploadFiles: UploadFiles) => Awaitable<boolean>;
    onExceed?: (files: File[], uploadFiles: UploadUserFile[]) => void;
  }>(),
  {
    modelValue: () => [],
    size: 'default',
    type: 'dropdown',
    itemType: 'card',
    direction: 'vertical',
    supportPreview: true,
    supportDownload: true,
    limit: 0,
    disabled: false,
    action: '',
    headers: () => ({}),
    multiple: false,
    data: () => ({}),
    name: 'file',
  },
);

const showPopover = ref(false);
const showPreview = ref(false);
const fileIndex = ref(0);
const isLoading = ref(false);
const uploadWidget = useUploadWidget(props.limit);
const fileList = computed(() => {
  return (props.modelValue || []).map(item => {
    return {
      ...item,
      type: getFileType(item.name),
    };
  });
});

const showCardAddBtn = computed(() => {
  let temp =
    props.itemType === 'card' &&
    (props.limit === 0 || props.limit == null || fileList.value.length < props.limit);
  console.log('temp', temp);
  return temp;
});

const emit = defineEmits(['update:modelValue', 'error', 'success', 'remove', 'change']);

const onPreviewClick = (file, fileList) => {
  if (props.supportPreview) {
    fileIndex.value = fileList.findIndex(item => item.url === file.url);
    nextTick(() => {
      showPreview.value = true;
    });
  } else if (props.supportDownload) {
    console.log('file', file);
    downloadFile(file.url, file.name);
  }
};

const onUploadSuccess = (response, file, fileList) => {
  isLoading.value = false;
  if (response.success) {
    console.log('response', response);
    file.downloadUri = response.data.downloadUri;
    file.filename = response.data.filename;
    file.uploadPath = response.data.uploadPath;
    // file.url = URL.createObjectURL(file.raw);
    file.url = response.data.downloadUri;
    uploadWidget.onFileChange(file, fileList);
    console.log('uploadWidget.fileList', uploadWidget.fileList.value);
    onChange(uploadWidget.fileList.value);
    emit('success', response, file, fileList);
  } else {
    ElMessage.error(response.message);
    emit('error', response, file, fileList);
  }
};

const onRemoveFile = (file, fileList) => {
  if (file != null && fileList != null) {
    fileList = fileList.filter(item => item.url !== file.url);
  }

  uploadWidget.onFileChange(file, fileList);
  onChange(uploadWidget.fileList.value);
  emit('remove', file, fileList);
};

const onUploadError = (e, file, fileList) => {
  isLoading.value = false;
  ElMessage.error('文件上传失败');
};

const onUploadLimit = (file, fileList) => {
  if (props.onExceed && typeof props.onExceed === 'function') {
    props.onExceed(file, fileList);
  } else {
    ElMessage.error('已经超出最大上传个数限制');
  }
};

const onDeleteFile = (file, fileList) => {
  uploadWidget.onFileChange(file, fileList);
  onChange(uploadWidget.fileList.value);
  emit('remove', file, fileList);
};

const uploadBtn = ref<HTMLElement | null>(null);
const onAddFile = () => {
  if (uploadBtn.value) {
    uploadBtn.value.click();
  }
};

const onChange = value => {
  emit('update:modelValue', value);
  emit('change', value);
};

const beforeUpload = (rawFile) => {
  isLoading.value = true;
  if (props.beforeUpload && typeof props.beforeUpload === 'function') {
    return props.beforeUpload(rawFile);
  }
  return true;
};

watch(
  () => props.modelValue,
  val => {
    uploadWidget.fileList.value = val;
  },
  { immediate: true },
);
</script>

<style lang="scss" scoped>
.custom-upload .el-dropdown ::v-deep .el-button-group .el-button:first-child {
  padding: 0px;
}

.upload-btn {
  display: flex;
  align-items: center;
  padding: 9px 16px;
}

.upload-btn .file-count {
  height: 12px;
  line-height: 12px;
  min-width: 12px;
  color: white;
  font-size: 12px;
  background: #f56c6c;
  border-radius: 6px;
  padding: 0px 3px;
  margin-left: 5px;
}

.hide-list ::v-deep .el-dropdown__caret-button {
  display: none;
}

.dropdown-file-item {
  width: 100%;
  min-width: 200px;
  height: 36px;
  line-height: 36px;
}

.dropdown-file-item span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.expand-upload.card {
  height: 0px !important;
}

::v-deep .el-dropdown-menu__item {
  padding: 0px 12px;
}

::v-deep .el-dropdown-menu__item:not(.is-disabled):hover,
::v-deep .el-dropdown-menu__item:not(.is-disabled):hover i,
::v-deep .el-dropdown-menu__item:focus,
::v-deep .el-dropdown-menu__item:focus i {
  background-color: white;
  color: $color-primary;
}

::v-deep .el-dropdown-menu__item i {
  color: #d8d8d8;
}

.is-loading {
  margin-right: 4px;
}
</style>
